﻿Imports System.Windows.Controls


Public Class ViewIndicateurFormula

    Private Sub FormulaTextBox_TextChanged(ByVal sender As Object, ByVal e As Windows.Controls.TextChangedEventArgs) Handles FormulaTextBox.TextChanged

    End Sub


    Private Sub ColumnsListBoxItemMouseDoubleClick(ByVal sender As Object, ByVal e As Windows.Input.MouseButtonEventArgs)
        Dim col As DataColumn = CType(sender, ListBoxItem).DataContext

        Dim txt = "Champs(""" & col.Table.TableName & """,""" & col.ColumnName & """)"

        Dim nextcaretindex = FormulaTextBox.CaretIndex + txt.Length

        FormulaTextBox.Text = _
                  FormulaTextBox.Text.Insert( _
                      FormulaTextBox.CaretIndex, _
                      txt)

        FormulaTextBox.CaretIndex = nextcaretindex

    End Sub

    Private Sub ChildTableColumnsListBoxItemMouseDoubleClick(ByVal sender As Object, ByVal e As Windows.Input.MouseButtonEventArgs)
        Dim col As DataColumn = CType(sender, ListBoxItem).DataContext
        Dim rel As DataRelation = ChildRelationsListBox.SelectedItem

        Dim txt = "Champs(""" & col.Table.TableName & """,""" & rel.RelationName & """,""" & col.ColumnName & """)"

        FormulaTextBox.Text = _
                  FormulaTextBox.Text.Insert( _
                      FormulaTextBox.CaretIndex, _
                      txt)



    End Sub

    Private Sub ParentTableColumnsListBoxItemMouseDoubleClick(ByVal sender As Object, ByVal e As Windows.Input.MouseButtonEventArgs)
        Dim col As DataColumn = CType(sender, ListBoxItem).DataContext
        Dim rel As DataRelation = ParentRelationsListBox.SelectedItem

        Dim txt = "Champs(""" & rel.ChildTable.TableName & """,""" & rel.RelationName & """,""" & col.ColumnName & """)"

        FormulaTextBox.Text = _
                  FormulaTextBox.Text.Insert( _
                      FormulaTextBox.CaretIndex, _
                      txt)

    End Sub
End Class

Public Class ViewModelIndicateurFormula
    Inherits UIObject
    Implements ComponentModel.IDataErrorInfo

    Public ReadOnly Property Tables As IEnumerable
        Get
            Return ModuleDashboarding._activeproject.DataSet.Tables
        End Get
    End Property

    Private _Formula As String
    Public Property Formula As String
        Get
            Return _Formula
        End Get
        Set(ByVal value As String)
            _Formula = value
            OnPropertyChanged("Formula")
        End Set
    End Property

    Private _Name As String
    Public Property Name As String
        Get
            Return _Name
        End Get
        Set(ByVal value As String)
            _Name = value
            OnPropertyChanged("Name")
        End Set
    End Property

    ' Private _Expression As FormulaExpression
    Public Function GetExpression() As XElement

        Dim vb = New VBCodeProvider()
        Dim str = New IO.StringWriter()
        Dim unit = New CodeDom.CodeCompileUnit()
        Dim ns = New CodeDom.CodeNamespace()
        unit.Namespaces.Add(ns)
        ns.Imports.Add(New CodeDom.CodeNamespaceImport("OMANE.Modules.Dashboarding.FormulaExpression"))
        ns.Imports.Add(New CodeDom.CodeNamespaceImport("OMANE.Modules.Dashboarding"))
        ns.Imports.Add(New CodeDom.CodeNamespaceImport("System.Math"))
        Dim cls = New CodeDom.CodeTypeDeclaration("Formula")
        cls.IsClass = True
        cls.Attributes = CodeDom.MemberAttributes.Public Or CodeDom.MemberAttributes.Static Or CodeDom.MemberAttributes.Final
        ns.Types.Add(cls)
        Dim mem = New CodeDom.CodeMemberMethod()
        mem.Name = "GetExpression"
        mem.Attributes = CodeDom.MemberAttributes.Public Or CodeDom.MemberAttributes.Final Or CodeDom.MemberAttributes.Static
        mem.ReturnType = New CodeDom.CodeTypeReference(GetType(Object))
        Dim code = "dim result  as FormulaExpression" & _Formula & vbCrLf & " return result "
        mem.Statements.Add(New CodeDom.CodeSnippetExpression(code))
        cls.Members.Add(mem)
        'vb.GenerateCodeFromCompileUnit(unit, str, New CodeDom.Compiler.CodeGeneratorOptions())
        'ModuleDashboarding._uiservice.ShowMessage(str.ToString())
        Dim param = New CodeDom.Compiler.CompilerParameters()
        param.GenerateExecutable = False
        param.GenerateInMemory = True
        'param.OutputAssembly = Guid.NewGuid().ToString()
        param.IncludeDebugInformation = False
        param.ReferencedAssemblies.Add(Me.GetType().Assembly.Location)
        Dim compileresult = vb.CompileAssemblyFromDom(param, unit)

        ModuleDashboarding._uiservice.ShowMessage( _
    New TextBlock() With {.MaxWidth = 500, .Text = String.Join(vbCrLf, compileresult.Output.Cast(Of String).ToArray()), .TextWrapping = Windows.TextWrapping.Wrap}, _
            Width:=600)
        AddHandler AppDomain.CurrentDomain.AssemblyResolve, Function(sender As Object, args As ResolveEventArgs)
                                                                'Dim o = 1
                                                                Return Me.GetType().Assembly
                                                            End Function
        Return FormulaExpression.Save( _
            compileresult.CompiledAssembly.GetType("Formula").GetMethod("GetExpression").Invoke(Nothing, Nothing))


    End Function

    Public ReadOnly Property ValidateFormulaCommand As DelegateCommand
        Get
            Return New DelegateCommand(Sub()
                                           If Item("Formula") <> "" Then
                                               MsgBox("Expression Invalide " & vbCrLf & Item("Formula"), vbCritical)
                                               Return
                                           End If

                                           'Dim block As ICSharpCode.NRefactory.Ast.BlockStatement = parser.Parse("dim result  as object" & _Formula)
                                           'Dim decl As ICSharpCode.NRefactory.Ast.LocalVariableDeclaration = block.Children(0)
                                           'Dim f = decl.GetVariableDeclaration("result").Initializer
                                           'Try
                                           '    GetExpression()

                                           '    MsgBox("Formule Valide")
                                           'Catch ex As Exception
                                           '    MsgBox(ex.Message)
                                           'End Try
                                           'Return
                                       End Sub)
        End Get
    End Property

    Public ReadOnly Property [Error] As String Implements System.ComponentModel.IDataErrorInfo.Error
        Get
            Return ""
        End Get
    End Property

    Dim parser As New ICSharpCode.NRefactory.SnippetParser(ICSharpCode.NRefactory.SupportedLanguage.VBNet)
    Default Public ReadOnly Property Item(ByVal columnName As String) As String Implements System.ComponentModel.IDataErrorInfo.Item
        Get
            If columnName = "Name" Then
                If String.IsNullOrEmpty(_Name) Then Return "Le nom de l'indicateur ne  peut etre vide"
                Dim q = From i In ModuleDashboarding._activeproject.IndicateurList
                        Where i.Name.ToLower() = _Name.ToLower()
                        Select i Take 1

                If q.Count() > 0 Then
                    Return "Ce nom existe deja"
                End If

                Return ""
            End If

            If columnName = "Formula" Then
                If String.IsNullOrEmpty(_Formula) Then Return "La formule de l'indicateur ne  peut etre vide"

                parser.Parse("dim o  as object " & _Formula)

                If parser.Errors.Count > 0 Then

                    Return parser.Errors.ErrorOutput
                End If


                Return ""
            End If

            Return ""
        End Get
    End Property
End Class



Public NotInheritable Class FormulaExpression

    Private Sub New()

    End Sub

    Private Type As String
    Private Value As Object
    Private MemberTable As String
    Private MemberColumn As String
    Private MemberParentRelation As String
    Private Right As FormulaExpression
    Private Left As FormulaExpression
    Private [Operator] As String

 

    Public Shared Widening Operator CType(ByVal v As Double) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "constant"
        result.Value = v
        Return result
    End Operator
    Public Shared Widening Operator CType(ByVal v As String) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "constant"
        result.Value = v
        Return result
    End Operator
    Public Shared Widening Operator CType(ByVal v As DateTime) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "constant"
        result.Value = v
        Return result
    End Operator

    Public Shared Operator *(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "*"
        Return result
    End Operator
    Public Shared Operator /(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "/"
        Return result
    End Operator
    Public Shared Operator +(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "+"
        Return result
    End Operator
    Public Shared Operator -(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "-"
        Return result
    End Operator
    Public Shared Operator =(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "="
        Return result
    End Operator
    Public Shared Operator <>(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "<>"
        Return result
    End Operator
    Public Shared Operator >(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = ">"
        Return result
    End Operator
    Public Shared Operator <(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "<"
        Return result
    End Operator
    Public Shared Operator >=(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = ">="
        Return result
    End Operator
    Public Shared Operator <=(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "<="
        Return result
    End Operator
    Public Shared Operator ^(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "^"
        Return result
    End Operator
    Public Shared Operator And(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "And"
        Return result
    End Operator
    Public Shared Operator Or(ByVal left As FormulaExpression, ByVal right As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "binary"
        result.Left = left
        result.Right = right
        result.Operator = "Or"
        Return result
    End Operator
    Public Shared Operator Not(ByVal left As FormulaExpression) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "unary"
        result.Left = left
        result.Operator = "Not"
        Return result
    End Operator

    Public Shared Function Champs(ByVal table As String, ByVal column As String) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "member"
        result.MemberTable = table
        result.MemberColumn = column
        Return result
    End Function
    Public Shared Function Champs(ByVal table As String, ByVal relation As String, ByVal column As String) As FormulaExpression
        Dim result As New FormulaExpression
        result.Type = "member"
        result.MemberTable = table
        result.MemberParentRelation = relation
        result.MemberColumn = column
        Return result
    End Function

    Public Shared Function GetSql(ByVal ParamArray groupColumns() As String) As String

    End Function

    Public Shared Function AddToTable(ByVal expr As FormulaExpression, ByVal table As DataTable, ByRef coltofilter As List(Of DataColumn)) As DataColumn
        Return table.Columns.Add("tempcol", GetType(Double), GetTableString(expr, table, coltofilter))
    End Function
    Public Shared Function GetTableString(ByVal expr As FormulaExpression, ByVal table As DataTable, ByRef coltofilter As List(Of DataColumn)) As String
        If expr.Type = "constant" Then Return expr.Value.ToString()

        If expr.Type = "member" Then
            Dim q = (From rel As DataRelation In table.ChildRelations
                     Where rel.ChildTable.TableName = expr.MemberTable
                     Select rel Take 1).FirstOrDefault()
            If q Is Nothing Then
                Throw New Exception("Impossible de trouver la table " & expr.MemberTable)
            End If

            If expr.MemberParentRelation = "" Then
                If Not q.ChildTable.Columns.Contains(expr.MemberColumn) Then
                    Throw New Exception("Impossible de trouver la colonne " & expr.MemberColumn & " dans la table " & expr.MemberTable)
                End If

                coltofilter.Add(q.ChildTable.Columns(expr.MemberColumn))
                Return "SUM(Child(" & q.RelationName & ")._Filter_" & expr.MemberColumn & ")"
            Else
                'Return Champs(expr.MemberTable, expr.MemberParentRelation, expr.MemberColumn)
            End If
        End If

        If expr.Type = "unary" Then Return "(" & expr.Operator & " " & GetTableString(expr.Left, table, coltofilter) & ")"

        If expr.Type = "binary" Then Return "(" & GetTableString(expr.Left, table, coltofilter) & " " & expr.Operator & " " & GetTableString(expr.Right, table, coltofilter) & ")"
        Return ""
    End Function

    Public Shared Function Save(ByVal expr As FormulaExpression) As XElement
        If expr.Type = "constant" Then Return <Constant Value=<%= expr.Value %> Type=<%= expr.Value.GetType().Name %>/>

        If expr.Type = "member" Then Return <Member
                                                MemberTable=<%= expr.MemberTable %>
                                                MemberParentRelation=<%= expr.MemberParentRelation %>
                                                MemberColumn=<%= expr.MemberColumn %>/>

        If expr.Type = "unary" Then Return <Unary
                                               Operator=<%= expr.Operator %>>
                                               <%= Save(expr.Left) %>
                                           </Unary>

        If expr.Type = "binary" Then Return <Binary
                                                Operator=<%= expr.Operator %>>
                                                <%= Save(expr.Left) %>
                                                <%= Save(expr.Right) %>
                                            </Binary>
        Return <Empty/>
    End Function
    Public Shared Function Load(ByVal x As XElement) As FormulaExpression
        If x.Name = "Constant" Then
            If x.@Type = "String" Then Return CStr(x.@Value)
            If x.@Type = "Double" Then Return Double.Parse(x.@Value, Globalization.CultureInfo.InvariantCulture)
            If x.@Type = "DateTime" Then Return CDate(x.@Value)
        End If

        If x.Name = "member" Then
            If x.@MemberParentRelation = "" Then
                Return Champs(x.@MemberTable, x.@MemberColumn)
            Else
                Return Champs(x.@MemberTable, x.@MemberParentRelation, x.@MemberColumn)
            End If
        End If

        If x.Name = "unary" Then
            Dim result As New FormulaExpression
            result.Operator = x.@Operator
            result.Type = "unary"
            result.Left = Load(x.Elements.ElementAt(0))
            Return result
        End If

        If x.Name = "binary" Then
            Dim result As New FormulaExpression
            result.Operator = x.@Operator
            result.Type = "binary"
            result.Left = Load(x.Elements.ElementAt(0))
            result.Right = Load(x.Elements.ElementAt(1))
            Return result
        End If
        Return Nothing
    End Function
End Class
